#pragma once

#include "znx_defs.h"
#include "znx_crc.h"
#include "znx_queue.h"
#include "znx_rbtree.h"
#include "znx_string.h"
#include "znx_thread_base.h"

typedef struct {
    znx_str_t                   key;
    void                        *data;
    int                         weight;
    znx_queue_t                 queue;
} znx_chash_node_t;


static inline void
znx_chash_node_init(znx_chash_node_t *node,
    znx_str_t *key, void *data, int weight)
{
    assert(key != NULL);
    node->key = *key;
    node->data = data;
    node->weight = weight;
}

typedef struct {
    znx_rbtree_node_t           tree_node;
    uint32_t                    crc_key;
    znx_chash_node_t            *real_node;
} znx_chash_vnode_t;


static inline void
znx_chash_vnode_init(znx_chash_vnode_t *vnode,
    znx_chash_node_t *node, int idx)
{
    char buf[64] = { 0 };
    uint32_t crc1, crc2;

    crc1 = znx_crc32_short((const u_char *)node->key.data, (size_t)node->key.len);
    int n = snprintf(buf, 64, "#%d-%u-%d#", idx, crc1, idx);

    znx_crc32_init(crc2);
    znx_crc32_update(&crc2, (const u_char *)buf, (size_t)n);
    znx_crc32_update(&crc2, (const u_char *)node->key.data, (size_t)node->key.len);
    znx_crc32_update(&crc2, (const u_char *)buf, (size_t)n);
    znx_crc32_final(crc2);

    vnode->crc_key = crc2;
    vnode->real_node = node;
}


static inline int
znx_chash_vnode_cmp(znx_rbtree_node_t *a, znx_rbtree_node_t *b)
{
    znx_chash_vnode_t *anode = (znx_chash_vnode_t *)a;
    znx_chash_vnode_t *bnode = (znx_chash_vnode_t *)b;

    if (anode->crc_key == bnode->crc_key) {
        return 0;
    }

    return anode->crc_key > bnode->crc_key ? 1 : -1;
}


typedef struct {
    int                         replace;
    int                         nnode;
    znx_queue_t                 head;
    znx_rbtree_t                tree;
    znx_rbtree_node_t           sentinel;
    znx_chash_node_t            **nodes;
    int64_t                     total_weight;
} znx_chash_t;


#define ZNX_CHASH_DEF_REPLACE    (256)

znx_chash_t *znx_chash_create(int replace);

void znx_chash_add_node(znx_chash_t *chash, znx_str_t *key, void *data, int weight);

void znx_chash_build(znx_chash_t *chash);

typedef znx_bool_t (*znx_chash_node_check_pt) (znx_chash_node_t *node);

void *znx_chash_get(znx_chash_t *chash, znx_str_t *key,
    znx_chash_node_check_pt checker, znx_bool_t must_get);

void znx_chash_destroy(znx_chash_t *chash);

